																												package dongchel.vrp.immunes;

import java.util.ArrayList;
import java.util.List;

import sun.misc.Sort;

import dongchel.vrp.bbinsertion.SPDCityInsertion;
import dongchel.vrp.bbinsertion.SPDObjectiveValue;
import dongchel.vrp.clusterandroute.Cluster;
import dongchel.vrp.data.LoadData;
import dongchel.vrp.data.WriteData;
import dongchel.vrp.entity.City;
import dongchel.vrp.entity.GlobalInformation;
import dongchel.vrp.entity.Route;
import dongchel.vrp.entity.Solution;
import dongchel.vrp.general.CompareObject;
import dongchel.vrp.general.InitialSolution;
import dongchel.vrp.general.vrpinterface.ObjectivePenaltyInterface;
import dongchel.vrp.rachelinsertion.Insertion;
import dongchel.vrp.rachelinsertion.RachelInsertion;
import dongchel.vrp.rachelinsertion.RachelObjectiveValue;
import dongchel.vrp.utils.CollectionUtils;
import dongchel.vrp.view.ShowSolution;
import dongchel.vrp.view.TrendChart;

public class Immunes {
	private Insertion insertion = new SPDCityInsertion();
	private ObjectivePenaltyInterface opi = new SPDObjectiveValue();

	int populationSize = 100;
	int cloneSize = 25;
	int memorySize = 10;
	int totalIteration = 50;
	private List<Solution> initialPopulation = new ArrayList<Solution>();
	private List<Solution> population = new ArrayList<Solution>();
	private List<Solution> clonedPopulation = new ArrayList<Solution>();
	private List<Solution> hypermutatedPopulation = new ArrayList<Solution>();
	private List<Solution> memoryPopulation = new ArrayList<Solution>();
	private List<Solution> suppressedPopulation = new ArrayList<Solution>();
	
	public Immunes(int totalIteration) {
		this.totalIteration = totalIteration;
	}

	public void initial() {
		Clone clone = new Clone();
		Memory memory = new Memory(memoryPopulation);
		//initiate the initial population, cloned population, hypermutated population, memory population
		//by angle, put cities in order,  generate cluster
		//1.generate an initial population
		for(int k=0;k<populationSize;k++) {
			Solution solution = new Solution(opi,insertion);
			solution.sortUnvisitedCitesByAngle();
			List<Cluster> clusterList = new ArrayList<Cluster>();
			if(k<solution.getUnvisitedCities().size()) {
				clusterList = solution.generateClusters(k);
			}
			else {
				clusterList = solution.generateClusters(k%solution.getUnvisitedCities().size());
			}
			//random generate route
			for(int i=0;i<clusterList.size();i++){
				Route route = new Route(solution);
				Cluster cluster = clusterList.get(i);
				for(int j=0;j<cluster.getCityList().size();j++){
					City city = cluster.getCityList().get(j);
					int insertPosition = (int)Math.floor(Math.random()*(route.getCityList().size()-1))+1;
					route.insertion(city, insertPosition);
				}
			}
			initialPopulation.add(solution);
		}

		//2.sort according to Affinity: priority is ---  capacity violation, total distance, vehicle number
		SortPopulation sortP = new SortPopulation(initialPopulation);
		sortP.sortPopulation();
		
		//3.construct the memory population
		List<CompareObject> initialCompareObjectList = sortP.getCompareObjectList();
		memoryPopulation = memory.constructMemory(initialCompareObjectList);
		
		//4.select cloneSize best individuals of the population, clone according to Affinity and store into cloned population
		//4.1 clone10Times
	/*	List<CompareObject> compareObjectList = sortP.getCompareObjectList();
		List<Solution> newcloneList = new ArrayList<Solution>();
		clonedPopulation = clone.clone10Times(newcloneList, compareObjectList);*/
	
		//4.2 cloneByAffinity
		Affinity affi = new Affinity(initialPopulation);
		List<Double> accumAffi = affi.accumulateAffinity();
		List<Solution> newcloneList = new ArrayList<Solution>();
		clonedPopulation = clone.cloneByAffinity(newcloneList, initialPopulation, accumAffi);
		
		//5.mutate inversely proportional to Affinity, save to hypermutatedPopulation
		Affinity affin = new Affinity(clonedPopulation);
		Mutation mutation = new Mutation(clonedPopulation, affin.getAffinity());
		mutation.hypermutatePopulation();
		for(int i=0;i<mutation.getMutatedPopulation().size();i++) {
			hypermutatedPopulation.add(mutation.getMutatedPopulation().get(i));
		}
		//6.select the best ones (according to Affinity) and add into population
		sortP = new SortPopulation(hypermutatedPopulation);
		sortP.sortPopulation();
		/*select populationSize best individuals of the population, clone according to Affinity 
		and store into population*/
		for(int i=0;i<populationSize;i++) {
			Solution solu = (Solution)sortP.getCompareObjectList().get(i).getObject();
			population.add((Solution)solu.clone());
		}
	}

	public void immunes(){
		//initialise
		initial();
		int iter = 0;
		Memory memory = new Memory(memoryPopulation);
		Clone clone = new Clone();
		List<Solution> newComing = new ArrayList<Solution>();
		//prepare for chart
		TrendChart trendChart = new TrendChart();
		List<Double> data = new ArrayList<Double>();

		while(iter<totalIteration) {
			System.out.println("iteration = "+iter);
			//1. sort according to Affinity: priority is ---  capacity violation, total distance, vehicle number
			SortPopulation sortP = new SortPopulation(population);
			sortP.sortPopulation();
			//2. retain the best 5 into memory population
			if(newComing.size()==0){
				for(int i=0;i<memorySize;i++) {
					Solution solu = (Solution)sortP.getCompareObjectList().get(i).getObject();
					newComing.add((Solution)solu.clone());
				}
			}
			else {
				for(int i=0;i<memorySize;i++) {
					Solution solu = (Solution)sortP.getCompareObjectList().get(i).getObject();
					newComing.set(i, (Solution)solu.clone());
				}
			}
			//3.1 select cloneSize individuals of the population, clone according to Affinity and store into cloned population
			/*List<CompareObject> compareObjectList = sortP.getCompareObjectList();
			List<Solution> newcloneList = new ArrayList<Solution>();
			clonedPopulation = clone.clone10Times(newcloneList, compareObjectList);*/
			//3.2 cloneByAffinity, for mutate

			Affinity affi = new Affinity(population);
			List<Double> accumAffi = affi.accumulateAffinity();
			List<Solution> newcloneList = new ArrayList<Solution>();
			clonedPopulation = clone.cloneByAffinity(newcloneList, population, accumAffi);

			//4. mutate inversely proportional to Affinity, save to hypermutatedPopulation

			Affinity affin = new Affinity(clonedPopulation);
			Mutation mutation = new Mutation(clonedPopulation, affin.getAffinity());
			mutation.hypermutatePopulation();
			for(int i=0;i<mutation.getMutatedPopulation().size();i++) {
				hypermutatedPopulation.set(i, mutation.getMutatedPopulation().get(i));
			}

			//5.suppression
			
			Suppression suppression = new Suppression(hypermutatedPopulation);
			suppressedPopulation = suppression.suppressByTotalDistance();	

			
			//6. select the best ones (according to Affinity) and copy into population
			sortP = new SortPopulation(suppressedPopulation);
			sortP.sortPopulation();
			/*select best individuals of the hypermutatedPopulation, clone according to Affinity 
		and store into population*/
			for(int i=0;i<populationSize;i++) {
				Solution solu = (Solution)sortP.getCompareObjectList().get(i).getObject();
				population.set(i, (Solution)solu.clone());
			}

			//7. update the memory population
			if(newComing.size()==memorySize){
				for(int i=0;i<memorySize;i++) {
					Solution solu = (Solution)sortP.getCompareObjectList().get(i).getObject();
					newComing.add((Solution)solu.clone());
				}
			}
			else {
				for(int i=0;i<memorySize;i++) {
					Solution solu = (Solution)sortP.getCompareObjectList().get(i).getObject();
					newComing.set(i+memorySize, (Solution)solu.clone());
				}
			}
			memory.updateMemory(newComing);

			//8. add data for showing in every iteration
			Solution solut = memory.getMemoryPopulation().get(0);
			data.add(solut.getObjectiveValue().getObjectiveList().get(1));
			iter++;
		}
		//print result
		trendChart.addData(data, "rough AIS solving VRP_SPD");
		trendChart.showData();
		ShowSolution.showSolution(memoryPopulation.get(0), 2);
		for(int i=0;i<memoryPopulation.size();i++){

			CollectionUtils.printList(memoryPopulation.get(i).getObjectiveValue().getObjectiveList());
		}
	}
}
